home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Files / MoreIsBetter / MIB-Libraries / Sources / MoreControls.cp < prev    next >
Encoding:
Text File  |  1998-09-25  |  47.2 KB  |  1,828 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        MoreControls.cp
  3.  
  4.     Contains:    
  5.  
  6.     Written by:    Pete Gontier (PCG)
  7.  
  8.     Copyright:    Copyright (c) 1998 Apple Computer, Inc.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <4>      9/1/98    PCG     Universal Headers 3.2
  13.          <3>     7/24/98    PCG        eliminate dependency on 'qd'
  14.          <2>     7/21/98    PCG     remove annoying assert in Get1NewStaticTextControl
  15.          <2>     6/23/98    PCG     add IsScrollBar
  16.          <1>     6/16/98    PCG     initial checkin
  17. */
  18.  
  19.  
  20. #include "MoreAppearance.h"
  21. #include "MoreControls.h"
  22.  
  23. #include <Script.h>
  24. #include <MacWindows.h>
  25. #include <Resources.h>
  26.  
  27. typedef struct
  28. {
  29.     //
  30.     //    Used by AppendTabInfo and its callers.
  31.     //    Caches tab contents data temporarily.
  32.     //
  33.  
  34.     Boolean                enabled;
  35.     ControlTabInfoRec    ctir;
  36. }
  37. FullControlTabInfoRec, *FullControlTabInfoRecP, **FullControlTabInfoRecH;
  38.  
  39.     //
  40.     //    gControlScratchStr is used to relieve us from having to declare
  41.     //    a Str255 on the stack when we call GetResInfo and we don't care
  42.     //    about the name of the resource.
  43.     //
  44.  
  45. static Str255 gControlScratchStr;
  46.  
  47.     //
  48.     //    AssertControlHasDefProcResID is a macro which asserts that
  49.     //    a given control's control definition procedure resource has
  50.     //    a given resource ID. This is generally used to validate
  51.     //    assumptions about the data tags supported by a given
  52.     //    control definition procedure.
  53.     //
  54.  
  55. #if MORE_DEBUG
  56.     static pascal Boolean MoreAssertControlHasDefProcResID
  57.         (ControlHandle c, short requiredDPR)
  58.     {
  59.         SInt16 actualDPR;
  60.  
  61.         if (!MoreAssert (!GetControlDefProcResID (c,&actualDPR)))    return false;
  62.         if (!MoreAssert (actualDPR == requiredDPR))                    return false;
  63.  
  64.         return true;
  65.     }
  66. #else
  67. #    define MoreAssertControlHasDefProcResID(c,requiredDPR) (true)
  68. #endif
  69.  
  70. pascal OSErr GetControlDefProcResID (ControlHandle control, short *resID)
  71. {
  72.     //
  73.     //    Given a control, determines what the resource ID of the control's
  74.     //    control definition procedure resource. If the resource has been
  75.     //    detached, this routine will fail and return an appropriate error.
  76.     //    Don't detach system definition procedure resources! Other people
  77.     //    may be using them as resources (expecting LoadResource to work, etc.)
  78.     //
  79.  
  80.     ResType resType;
  81.     GetResInfo ((**control).contrlDefProc, resID, &resType, gControlScratchStr);
  82.     OSErr err = ResError ( );
  83.     (void) MoreAssert (err || resType == kControlDefProcType);
  84.     return err;
  85. }
  86.  
  87. static pascal MoreControls_tAlignment FilterAlignment (MoreControls_tAlignment align)
  88. {
  89.     //
  90.     //    If this function is passed MoreControls_kAlignSystem, it determines
  91.     //    what the correct alignment constant is based on the direction of the
  92.     //    system script. This way radio buttons on an Arabic system, whose
  93.     //    bubbles are on the right side, will align down the right edge without
  94.     //    the client program needing to know the details.
  95.     //
  96.  
  97.     if (align == MoreControls_kAlignSystem)
  98.         align = GetSysDirection ( ) == -1 ? MoreControls_kAlignRight : MoreControls_kAlignLeft;
  99.  
  100.     (void) MoreAssert (align == MoreControls_kAlignLeft ||
  101.         align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight);
  102.  
  103.     return align;
  104. }
  105.  
  106. #pragma mark -
  107.  
  108. static pascal OSErr AppendTabInfo
  109.     (ControlHandle whichControl, UInt16 tabIndex,
  110.         FullControlTabInfoRecP tabInfoP, FullControlTabInfoRecH tabInfoPackedArrayH)
  111. {
  112.     OSErr err = noErr;
  113.  
  114.     //
  115.     //    For a given tab, this function gets the tab icon and string as
  116.     //    well whether the tab is enabled. Appends said data onto the end
  117.     //    of an array stored in a handle.
  118.     //
  119.     //    This is a helper function. tabInfoP is supposed to have been allocated
  120.     //    by the caller, but the caller is not expected to care what's in it;
  121.     //    we do this so the caller can call AppendTabInfo in a loop without
  122.     //    this helper function repeatedly creating and destroying the buffer,
  123.     //    which would be slow, we presume. It's a classic case of optimizing without
  124.     //    first measuring.
  125.     //
  126.  
  127.     if (!(MoreAssert (tabIndex && tabIndex <= GetControlMaximum (whichControl))))
  128.         err = paramErr;
  129.     else
  130.     {
  131.         Size actualSize;
  132.  
  133.         tabInfoP->ctir.version = 0;
  134.  
  135.         if (!(err = GetControlData (whichControl, tabIndex, kControlTabInfoTag,
  136.             sizeof (tabInfoP->ctir), Ptr (&(tabInfoP->ctir)), &actualSize)))
  137.         {
  138.             if (!(err = GetControlData (whichControl, tabIndex, kControlTabEnabledFlagTag,
  139.                 sizeof (tabInfoP->enabled), Ptr (&(tabInfoP->enabled)), &actualSize)))
  140.             {
  141.                 if (!MoreAssert (actualSize == sizeof (tabInfoP->enabled)))
  142.                     err = paramErr;
  143.                 else
  144.                     err = PtrAndHand (tabInfoP, Handle (tabInfoPackedArrayH), 1 + *(tabInfoP->ctir.name) + (sizeof (*tabInfoP) - sizeof (Str255)));
  145.             }
  146.         }
  147.     }
  148.  
  149.     return err;
  150. }
  151.  
  152. pascal OSErr MoreGetControlRegion (ControlHandle control, RgnHandle *rgn)
  153. {
  154.     //
  155.     //    Given a control, allocates and initializes a region describing the
  156.     //    control's bounds. Note this is different from (**control).contrlRect;
  157.     //    a control whose control definition function calls DrawThemeEditTextFrame
  158.     //    will (should) report its region as being 2 pixels larger than its contrlRect.
  159.     //
  160.  
  161.     OSErr err = noErr;
  162.  
  163.     if (!MoreAssert (HaveAppearance ( )))
  164.         err = paramErr;
  165.     if (!MoreAssert (control && *control && rgn))
  166.         err = nilHandleErr;
  167.     else
  168.     {
  169.         *rgn = NewRgn ( );
  170.  
  171.         if (!(err = QDError ( )))
  172.             (void) SendControlMessage (control, calcCntlRgn, SInt32 (*rgn));
  173.     }
  174.  
  175.     return err;
  176. }
  177.  
  178. pascal OSErr InvalControl (ControlHandle control)
  179. {
  180.     OSErr err = noErr;
  181.  
  182.     //
  183.     //    Given a control, invalidates the area occupied by the control.
  184.     //
  185.  
  186.     if (!(MoreAssert (control && *control)))
  187.         err = nilHandleErr;
  188.     else
  189.     {
  190.         RgnHandle rgn;
  191.  
  192.         err = MoreGetControlRegion (control, &rgn);
  193.  
  194.         if (!err)
  195.         {
  196.             GrafPtr preservePort;
  197.             GetPort (&preservePort);
  198.             SetPort ((**control).contrlOwner);
  199.             InvalRgn (rgn);
  200.             SetPort (preservePort);
  201.             DisposeRgn (rgn);
  202.         }
  203.     }
  204.  
  205.     return err;
  206. }
  207.  
  208. pascal OSErr SetTabIcon (ControlHandle control, ControlPartCode part, short iconSuiteID)
  209. {
  210.     OSErr err = noErr;
  211.  
  212.     //
  213.     //    This function demonstrates how to set the icon of a tab.
  214.     //    Unfortunately, under 1.0.X, the control definition won't
  215.     //    listen if the tab already has an icon which has been drawn.
  216.     //    We don't assert for this because it works if the tab has
  217.     //    no icon. Be careful.
  218.     //
  219.  
  220.     if (!MoreAssert (HaveAppearance ( )))
  221.         err = paramErr;
  222.     else if (!MoreAssert (control && *control))
  223.         err = nilHandleErr;
  224.     else if (!MoreAssert (part >= GetControlMinimum (control)))
  225.         err = paramErr;
  226.     else if (!(MoreAssert (part <= GetControlMaximum (control))))
  227.         err = paramErr;
  228.     else
  229.     {
  230.         ControlTabInfoRec *ctirp = (ControlTabInfoRec *) NewPtr (sizeof (*ctirp));
  231.  
  232.         if (!ctirp)
  233.             err = MemError ( );
  234.         else
  235.         {
  236.             Size actualSize;
  237.  
  238.             ctirp->version = 0;
  239.  
  240.             if (!(err = GetControlData (control,part,kControlTabInfoTag,sizeof(ControlTabInfoRec),
  241.                 Ptr(ctirp),&actualSize)))
  242.             {
  243.                 ctirp->iconSuiteID = iconSuiteID;
  244.  
  245.                 err = SetControlData (control,part,kControlTabInfoTag,actualSize,Ptr(ctirp));
  246.             }
  247.  
  248.             DisposePtr (Ptr (ctirp));
  249.             (void) MoreAssert (noErr == MemError ( ));
  250.         }
  251.     }
  252.  
  253.     return err;
  254. }
  255.  
  256. pascal OSErr CopyTabFontStyle (ControlHandle oldTabs, ControlHandle newTabs)
  257. {
  258.     //
  259.     //    Copies the control font style data from one tab control to another.
  260.     //
  261.  
  262.     OSErr err = noErr;
  263.  
  264.     if (!MoreAssert (HaveAppearance ( )))
  265.         err = paramErr;
  266.     else if (!MoreAssert (oldTabs && *oldTabs && newTabs && *newTabs))
  267.         err = nilHandleErr;
  268.     else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID))
  269.         err = paramErr;
  270.     else if (!MoreAssertControlHasDefProcResID (newTabs,kControlTabsDefProcResID))
  271.         err = paramErr;
  272.     else
  273.     {
  274.         ControlFontStyleRec        cfsr;
  275.         Size                    actualSize;
  276.  
  277.         if (!(err = GetControlData (oldTabs,kControlNoPart,kControlTabFontStyleTag,sizeof(cfsr),Ptr(&cfsr),&actualSize)))
  278.             err = SetControlData (newTabs,kControlNoPart,kControlTabFontStyleTag,actualSize,Ptr(&cfsr));
  279.     }
  280.  
  281.     return err;
  282. }
  283.  
  284. static pascal OSErr DupTabsControl_Internal (ControlHandle old, ControlHandle *dup, UInt16 newControlMax)
  285. {
  286.     //
  287.     //    This is a helper function containing common code to support
  288.     //    other exported functions. It creates a control with an
  289.     //    appropriate number of tabs, then embeds the control at
  290.     //    the same place in the hierarchy as the old control. This
  291.     //    function does not copy tab data from the old control to the
  292.     //    new control, though it does copy the style info.
  293.     //
  294.  
  295.     OSErr err = noErr;
  296.  
  297.     if (!MoreAssert (HaveAppearance ( )))
  298.         err = paramErr;
  299.     else if (!MoreAssert (GetAppearanceVersion ( ) >= 0x0101))
  300.         err = paramErr;
  301.     else if (!MoreAssert (old && *old && dup))
  302.         err = nilHandleErr;
  303.     else if (!MoreAssertControlHasDefProcResID (old,kControlTabsDefProcResID))
  304.         err = paramErr;
  305.     else if (!MoreAssert (GetControlMaximum (old) <= newControlMax))
  306.         err = paramErr;
  307.  
  308.     //
  309.     //    We assert that the old control is visible because there's a bug
  310.     //    in the tabs CDEF such that it won't write tab data into a non-visible
  311.     //    control. We copy tab data into the new control, so why do we
  312.     //    assert the old one's state? Because we are duplicating the old one to
  313.     //    the new one, and if the new one must be visible, it's not possible
  314.     //    to duplicate an invisible control (the old one) to a visible one
  315.     //    (the new one), because that wouldn't be duplication, now, would it?
  316.     //
  317.  
  318. //    else if (!MoreAssert (IsControlVisible (old)))
  319. //        err = paramErr;
  320.     else
  321.     {
  322.         ControlVariant        variant            = GetControlVariant (old);
  323.         Rect                boundsRect        = (**old).contrlRect;
  324.  
  325.         *dup = NewControl ((**old).contrlOwner, &boundsRect, "\p", IsControlVisible (old),
  326.             0, 1, newControlMax, (kControlTabsDefProcResID << 4) | variant, 0);
  327.         if (!*dup)
  328.             err = nilHandleErr;
  329.         else
  330.         {
  331.             if (!(err = CopyTabFontStyle (old,*dup)))
  332.             {
  333.                 //
  334.                 //    Embed duplicate control in old control's super-control.
  335.                 //    If the old control is in a window without an embedding
  336.                 //    hierarchy, do nothing.
  337.                 //
  338.  
  339.                 ControlHandle super;
  340.  
  341.                 if (!(err = GetSuperControl (old,&super)))
  342.                     err = EmbedControl (*dup,super);
  343.                 else if (err == errNoRootControl)
  344.                     err = noErr;
  345.  
  346.                 //
  347.                 //    Make sure the old and new controls have the same tab selected.
  348.                 //
  349.  
  350.                 if (!err)
  351.                     SetControlValue (*dup, GetControlValue (old));
  352.             }
  353.  
  354.             if (err)
  355.             {
  356.                 DisposeControl (*dup);
  357.                 *dup = nil;
  358.             }
  359.         }
  360.     }
  361.  
  362.     return err;
  363. }
  364.  
  365. static pascal OSErr CopyTab
  366.     (ControlHandle sourceControl, ControlHandle destControl, UInt16 sourceTab, UInt16 destTab)
  367. {
  368.     //
  369.     //    Copies the tab data from a single tab in one control to a single tab in another control.
  370.     //    This is a helper function which is designed to be called from inside a loop.
  371.     //
  372.  
  373.     OSErr err = noErr;
  374.  
  375.     if (!MoreAssert (HaveAppearance ( )))
  376.         err = paramErr;
  377.     else if (!MoreAssert (GetAppearanceVersion ( ) >= 0x0101))
  378.         err = paramErr;
  379.     else if (!MoreAssert (sourceControl && *sourceControl))
  380.         err = nilHandleErr;
  381.     else if (!MoreAssert (destControl && *destControl))
  382.         err = nilHandleErr;
  383.     else if (!MoreAssertControlHasDefProcResID (sourceControl,kControlTabsDefProcResID))
  384.         err = paramErr;
  385.     else if (!MoreAssertControlHasDefProcResID (destControl,kControlTabsDefProcResID))
  386.         err = paramErr;
  387.     else if (!MoreAssert (sourceTab <= GetControlMaximum (sourceControl)))
  388.         err = paramErr;
  389.     else if (!MoreAssert (destTab <= GetControlMaximum (destControl)))
  390.         err = paramErr;
  391.     else
  392.     {
  393.         Size                actualSize;
  394.         ControlTabInfoRec    *ctirp            = (ControlTabInfoRec *) NewPtr (sizeof (*ctirp));
  395.  
  396.         if (!ctirp)
  397.             err = MemError ( );
  398.         else
  399.         {
  400.             Boolean tabIsEnabled;
  401.  
  402.             ctirp->version = 0;
  403.  
  404.             do
  405.             {
  406.                 err = GetControlData (sourceControl,sourceTab,kControlTabInfoTag,sizeof(*ctirp),Ptr(ctirp),&actualSize);
  407.                 if (err) break;
  408.                 err = SetControlData (destControl,destTab,kControlTabInfoTag,actualSize,Ptr(ctirp));
  409.                 if (err) break;
  410.                 err = GetControlData (sourceControl,sourceTab,kControlTabEnabledFlagTag,sizeof(tabIsEnabled),Ptr(&tabIsEnabled),&actualSize);
  411.                 if (err) break;
  412.                 err = SetControlData (destControl,destTab,kControlTabEnabledFlagTag,actualSize,Ptr(&tabIsEnabled));
  413.                 if (err) break;
  414.             }
  415.             while (false);
  416.  
  417.             DisposePtr (Ptr (ctirp));
  418.             (void) MoreAssert (noErr == MemError ( ));
  419.         }
  420.     }
  421.  
  422.     return err;
  423. }
  424.  
  425. pascal OSErr InsertTab (ControlHandle oldTabs, ControlHandle *dup, UInt16 after)
  426. {
  427.     //
  428.     //    Adds a single tab to a tabs control. Since there are no APIs for adding
  429.     //    a tab (except at the end of the list [and that feature is buggy anyway]),
  430.     //    we duplicate the entire control with an additional tab, then copy the tab
  431.     //    data from the old control to the new control, leaving a gap for the new tab.
  432.     //    The after parameter, of course, specifies the tab after which the new tab
  433.     //    should appear. 0 is a valid value, even though it does not correspond to
  434.     //    any existing tab; it means the new tab should appear first. This function
  435.     //    also handles unreasonably high values by assuming you want a tab added after
  436.     //    the last existing tab.
  437.     //
  438.  
  439.     OSErr err = noErr;
  440.  
  441.     if (!(MoreAssert (oldTabs && *oldTabs && dup)))
  442.         err = nilHandleErr;
  443.     else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID))
  444.         err = paramErr;
  445.     else
  446.     {
  447.         UInt16 oldMax = GetControlMaximum (oldTabs);
  448.  
  449.         ControlHandle newTabs;
  450.  
  451.         if (!(err = DupTabsControl_Internal (oldTabs, &newTabs, oldMax + 1)))
  452.         {
  453.             if (UInt16 index = oldMax)
  454.             {
  455.                 if (after > index) after = index;
  456.  
  457.                 if (!(MoreAssert (index >= after)))
  458.                     err = paramErr;
  459.                 else
  460.                 {
  461.                     // copy the tab data for the tabs after the new one
  462.  
  463.                     while (index > after)
  464.                     {
  465.                         err = CopyTab (oldTabs,newTabs,index,index+1);
  466.                         if (err) break;                
  467.                         --index;
  468.                     }
  469.  
  470.                     // copy the tab data for the tabs before the new one
  471.  
  472.                     if (!err) while (index)
  473.                     {
  474.                         err = CopyTab (oldTabs,newTabs,index,index);
  475.                         if (err) break;
  476.                         --index;
  477.                     }
  478.                 }
  479.             }
  480.  
  481.             if (err)
  482.                 DisposeControl (newTabs);
  483.             else
  484.             {
  485.                 short oldValue = GetControlValue (oldTabs);
  486.  
  487.                 if (oldValue > after)
  488.                     SetControlValue (newTabs, oldValue + 1);
  489.  
  490.                 *dup = newTabs;
  491.             }
  492.         }
  493.     }
  494.  
  495.     return err;
  496. }
  497.  
  498. pascal OSErr RemoveTab (ControlHandle oldTabs, ControlHandle *dup, UInt16 victim)
  499. {
  500.     //
  501.     //    Removes a single tab from a tabs control. Since there are no APIs for
  502.     //    removing an arbitrary tab except at the end of the list, we duplicate
  503.     //    the entire control with one fewer tab, then copy the tab data from the
  504.     //    old control to the new control, skipping the doomed tab.
  505.     //
  506.  
  507.     OSErr err = noErr;
  508.  
  509.     if (!MoreAssert (victim && oldTabs && *oldTabs && dup))
  510.         err = nilHandleErr;
  511.     else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID))
  512.         err = paramErr;
  513.     else
  514.     {
  515.         UInt16 max = GetControlMaximum (oldTabs);
  516.  
  517.         if (!MoreAssert (max && max >= victim))
  518.             err = paramErr;
  519.         else
  520.         {
  521.             ControlHandle newTabs;
  522.  
  523.             if (!(err = DupTabsControl_Internal (oldTabs, &newTabs, max)))
  524.             {
  525.                 UInt16 index = 1;
  526.  
  527.                 // copy the tab data for the tabs before the victim
  528.  
  529.                 while (index < victim)
  530.                 {
  531.                     err = CopyTab (oldTabs,newTabs,index,index);
  532.                     if (err) break;
  533.                     ++index;
  534.                 }
  535.  
  536.                 // copy the tab data for the tabs after the victim
  537.  
  538.                 if (!err) while (index <= max - 1)
  539.                 {
  540.                     err = CopyTab (oldTabs, newTabs, index + 1, index);
  541.                     if (err) break;                
  542.                     ++index;
  543.                 }
  544.  
  545.                 if (err)
  546.                     DisposeControl (newTabs);
  547.                 else
  548.                 {
  549.                     SetControlMaximum (newTabs, max - 1);
  550.  
  551.                     short oldValue = GetControlValue (oldTabs);
  552.  
  553.                     if (oldValue > victim)
  554.                     {
  555.                         UInt16 newValue = oldValue - 1;
  556.  
  557.                         if (newValue != GetControlValue (newTabs))
  558.                             SetControlValue (newTabs, newValue);
  559.                     }
  560.  
  561.                     *dup = newTabs;
  562.                 }
  563.             }
  564.         }
  565.     }
  566.  
  567.     return err;
  568. }
  569.  
  570. pascal OSErr AppendTabs (ControlHandle control, UInt16 count)
  571. {
  572.     //
  573.     //    The tabs CDEF in Appearance 1.0.X has a fencepost
  574.     //    error in which it trashes its private tab data when
  575.     //    growing the tabs, so we detect this and refuse to
  576.     //    allow it to occur. If you need to add tabs to a tabs
  577.     //    control under Appearance 1.0.X, you need to use
  578.     //    InsertTab. The API is more cumbersome, but it does
  579.     //    work.
  580.     //
  581.  
  582.     OSErr err = noErr;
  583.  
  584.     if (!(MoreAssert (control && *control)))
  585.         err = nilHandleErr;
  586.     else if (!MoreAssertControlHasDefProcResID (control,kControlTabsDefProcResID))
  587.         err = paramErr;
  588.     else if (!MoreAssert (GetAppearanceVersion ( ) > 0x0101))
  589.         err = paramErr;
  590.     else
  591.     {
  592.         UInt16 max = GetControlMaximum (control);
  593.         SetControlMaximum (control, max + count);
  594.     }
  595.  
  596.     return err;
  597. }
  598.  
  599. pascal OSErr TruncateTabs (ControlHandle control, UInt16 count)
  600. {
  601.     //
  602.     //    The tabs CDEF in Appearance 1.0.X has a fencepost
  603.     //    error in which it trashes its private tab data when
  604.     //    shrinking the tabs, so we detect this and refuse to
  605.     //    allow it to occur. If you need to truncate tabs from
  606.     //    a tabs control under Appearance 1.0.X, you need to use
  607.     //    RemoveTab. The API is more cumbersome, but it does
  608.     //    work.
  609.     //
  610.  
  611.     OSErr err = noErr;
  612.  
  613.     if (!(MoreAssert (control && *control)))
  614.         err = nilHandleErr;
  615.     else if (!MoreAssertControlHasDefProcResID (control,kControlTabsDefProcResID))
  616.         err = paramErr;
  617.     else if (!MoreAssert (GetAppearanceVersion ( ) > 0x0101))
  618.         err = paramErr;
  619.     else
  620.     {
  621.         UInt16 max = GetControlMaximum (control);
  622.  
  623.         if (!MoreAssert (max && max >= count))
  624.             err = paramErr;
  625.         else
  626.             SetControlMaximum (control, max - count);
  627.     }
  628.  
  629.     return err;
  630. }
  631.  
  632. static pascal OSErr SpoofFindControlUnderMouseForTabs
  633.     (Point where, WindowRef window, ControlPartCode *cpc, ControlHandle whichControl)
  634. {
  635.     //
  636.     //    The 1.0.X Tabs control has a bug which causes
  637.     //    FindControlUnderMouse to produce kControlNoPart when the
  638.     //    mouse is over the current tab. The work-around is a quick
  639.     //    and dirty hack we can get away with because it only kicks
  640.     //    in under certain (old) versions of the API which we know
  641.     //    will never change. When this code was written, the bug had
  642.     //    been fixed in a pre-release version of Mac OS.
  643.     //    There's no iron-clad guarantee that version will ship with
  644.     //    the version number I expect it to, so there is some small but
  645.     //    non-zero chance this code may fail to work around the bug
  646.     //    if it is present in a system with a version of Appearance
  647.     //    greater than 1.0.1.
  648.     //
  649.  
  650.     OSErr err = noErr;
  651.  
  652.     if (*cpc == kControlNoPart && GetAppearanceVersion ( ) <= 0x0101)
  653.     {
  654.         SInt16 contrlValue = (**whichControl).contrlValue;
  655.         (**whichControl).contrlValue = 0;
  656.         ControlHandle newWhichControl =  FindControlUnderMouse (where,window,cpc);
  657.         (void) MoreAssert (newWhichControl == whichControl);
  658.         (**whichControl).contrlValue = contrlValue;
  659.     }
  660.  
  661.     return err;
  662. }
  663.  
  664. static pascal OSErr SpoofFindControlUnderMouseForPopUpButton (ControlPartCode *cpc)
  665. {
  666.     //
  667.     //    The popup button control has bugs which cause FindControlUnderMouse
  668.     //    to produce kControlNoPart when the mouse is over the title and
  669.     //    kControlLabelPart when the mouse is over the menu.
  670.     //
  671.  
  672.     OSErr err = noErr;
  673.  
  674.     if (*cpc == kControlLabelPart)
  675.         *cpc = kControlMenuPart;
  676.     else if (*cpc == kControlNoPart)
  677.         *cpc = kControlLabelPart;
  678.  
  679.     return err;
  680. }
  681.  
  682. pascal OSErr MoreFindControlUnderMouse
  683.     (Point where, WindowRef window, ControlPartCode *cpc, ControlHandle *whichControl)
  684. {
  685.     //
  686.     //    Second-guesses the results of FindControlUnderMouse for certain controls.
  687.     //    Decides which helper function to call based on the control definition
  688.     //    procedure resource ID. The truly interesting stuff happens in the helper
  689.     //    functions.
  690.     //
  691.  
  692.     OSErr err = noErr;
  693.  
  694.     if (HaveAppearance ( ))
  695.         *whichControl = FindControlUnderMouse (where,window,cpc);
  696.     else
  697.         *cpc = FindControl (where,window,whichControl);
  698.  
  699.     if (*whichControl)
  700.     {
  701.         if (!(MoreAssert (window == (***whichControl).contrlOwner)))
  702.             err = paramErr;
  703.         else
  704.         {
  705.             short resID;
  706.  
  707.             if (!(err = GetControlDefProcResID (*whichControl,&resID)))
  708.             {
  709.                 if (HaveAppearance ( ))
  710.                 {
  711.                     switch (resID)
  712.                     {
  713.                         case kControlTabsDefProcResID :
  714.  
  715.                             err = SpoofFindControlUnderMouseForTabs (where,window,cpc,*whichControl);
  716.                             break;
  717.  
  718.                         case kControlPopupButtonDefProcResID    :
  719.  
  720.                             err = SpoofFindControlUnderMouseForPopUpButton (cpc);
  721.                             break;
  722.                     }
  723.                 }
  724.                 else
  725.                 {
  726.                     // here we would work around bugs in pre-Appearance control definitions
  727.                 }
  728.             }
  729.         }
  730.     }
  731.  
  732.     return err;
  733. }
  734.  
  735. pascal OSErr Get1NewControl (short resID, WindowRef window, ControlHandle *control)
  736. {
  737.     //
  738.     //    Simple glue for Get1Resource. It only does two extra things. It verifies
  739.     //    the control resource with the specified exists in the topmost resource
  740.     //    file in the search chain and it releases the contol resource after
  741.     //    GetNewControl is done with it. I have an aversion to purgeable handles,
  742.     //    because they obfuscate leak detection, so I tend to write code like this.
  743.     //
  744.  
  745.     OSErr err = noErr;
  746.  
  747.     Handle controlResource = Get1Resource (kControlTemplateResourceType,resID);
  748.  
  749.     if (!controlResource)
  750.     {
  751.         err = ResError ( );
  752.         if (!err) err = resNotFound;
  753.     }
  754.     else
  755.     {
  756.         *control = GetNewControl (resID,window);
  757.  
  758.         if (!*control)
  759.             err = nilHandleErr;
  760.  
  761.         ReleaseResource (controlResource);
  762.         (void) MoreAssert (ResError ( ) == noErr);
  763.     }
  764.  
  765.     return err;
  766. }
  767.  
  768. pascal OSErr SetControlTextFromResource (ControlHandle control, short textResID)
  769. {
  770.     OSErr err = noErr;
  771.  
  772.     short controlDefProcResID;
  773.  
  774.     if (MoreAssert (!(err = GetControlDefProcResID (control,&controlDefProcResID))))
  775.     {
  776.         if (!MoreAssert (controlDefProcResID == kControlStaticTextDefProcResID || controlDefProcResID == kControlEditTextDefProcResID))
  777.             err = paramErr;
  778.         else
  779.         {    
  780.             Handle text = Get1Resource ('TEXT',textResID);
  781.  
  782.             if (!text)
  783.             {
  784.                 err = ResError ( );
  785.                 if (!err) err = resNotFound;
  786.             }
  787.             else
  788.             {
  789.                 Size size = InlineGetHandleSize (text);
  790.  
  791.                 if (MoreAssert (MemError ( ) == noErr))
  792.                 {
  793.                     HLock (text);
  794.  
  795.                     if (MoreAssert (MemError ( ) == noErr))
  796.                         err = SetControlData (control,kControlNoPart,kControlStaticTextTextTag,size,*text);
  797.                 }
  798.  
  799.                 ReleaseResource (text);
  800.                 (void) MoreAssert (ResError ( ) == noErr);
  801.             }
  802.         }
  803.     }
  804.  
  805.     return err;
  806. }
  807.  
  808. pascal OSErr SetControlTextStyleFromResource (ControlHandle control, short resID)
  809. {
  810.     OSErr err = noErr;
  811.  
  812.     Handle controlStyle = Get1Resource (kMoreControls_ControlStyleType, resID);
  813.  
  814.     if (!controlStyle)
  815.     {
  816.         err = ResError ( );
  817.         if (!err) err = resNotFound;
  818.     }
  819.     else
  820.     {
  821.         Size size = InlineGetHandleSize (controlStyle);
  822.         if (MoreAssert (MemError ( ) == noErr))
  823.         {
  824.             if (!MoreAssert (size == sizeof (ControlFontStyleRec)))
  825.                 err = paramErr;
  826.             else
  827.             {
  828.                 HLock (controlStyle);
  829.  
  830.                 if (MoreAssert (MemError ( ) == noErr))
  831.                     err = SetControlFontStyle (control, (const ControlFontStyleRec *) *controlStyle);
  832.             }
  833.         }
  834.  
  835.         ReleaseResource (controlStyle);
  836.         MoreAssert (ResError ( ) == noErr);
  837.     }
  838.  
  839.     return err;
  840. }
  841.  
  842. pascal OSErr Get1NewStaticTextControl (short resID, WindowRef window, ControlHandle *control)
  843. {
  844.     //
  845.     //    Since static text controls do not contain data describing their contents
  846.     //    or style, this function assumes the calling programmer has created (or chosen
  847.     //    not to) text and style resources with the same ID as the control resource.
  848.     //
  849.  
  850.     OSErr err = noErr;
  851.  
  852.     if (!MoreAssert (HaveAppearance ( )))
  853.         err = paramErr;
  854.     else if (!(err = Get1NewControl (resID,window,control)))
  855.     {
  856.         err = SetControlTextFromResource (*control,resID);
  857.  
  858.         if (err == resNotFound)
  859.             err = noErr;
  860.  
  861.         if (!err)
  862.         {
  863.             err = SetControlTextStyleFromResource (*control,resID);
  864.  
  865.             if (err == resNotFound)
  866.                 err = noErr;
  867.         }
  868.  
  869.         if (err)
  870.         {
  871.             DisposeControl (*control);
  872.             *control = nil;
  873.         }
  874.     }
  875.  
  876.     return err;
  877. }
  878.  
  879. pascal OSErr Get1NewPopupButtonControl (short resID, WindowRef window, ControlHandle *control)
  880. {
  881.     //
  882.     //    Some versions of Appearance (as of this writing, 1.0.2 and earlier)
  883.     //    have a bug in the popup menu button CDEF which causes a crash on
  884.     //    GetBestControlRect if the menu ID specified at creation of the control
  885.     //    does not exist (and is not -12345). Luckily, we can test for this
  886.     //    condition before it's too late. If the CDEF gets fixed, it's unlikely
  887.     //    that the fix will involve setting contrlData to NIL.
  888.     //
  889.  
  890.     OSErr err = noErr;
  891.  
  892.     if (!MoreAssert (HaveAppearance ( )))
  893.         err = paramErr;
  894.     else if (!(err = Get1NewControl (resID,window,control)))
  895.     {
  896.         if (!MoreAssertControlHasDefProcResID (*control,kControlPopupButtonDefProcResID))
  897.             err = paramErr;
  898.         else if (!MoreAssert ((***control).contrlData)) // will fire if non-existent menu ID specified
  899.             err = paramErr;
  900.  
  901.         if (err)
  902.             DisposeControl (*control);
  903.     }
  904.  
  905.     return err;
  906. }
  907.  
  908. pascal OSErr SetUserPaneDrawProc (ControlHandle control, ControlUserPaneDrawProcPtr upp)
  909. {
  910.     //
  911.     //    This is just convenience glue to avoid stringing a bunch of long
  912.     //    constant names together all the time.
  913.     //
  914.  
  915.     OSErr err = noErr;
  916.  
  917.     if (!MoreAssert (HaveAppearance ( )))
  918.         err = paramErr;
  919.     else if (!MoreAssertControlHasDefProcResID (control,kControlUserPaneDefProcResID))
  920.         err = paramErr;
  921.     else
  922.         err = SetControlData (control,kControlNoPart,kControlUserPaneDrawProcTag,sizeof(upp),(Ptr)&upp);
  923.  
  924.     return err;
  925. }
  926.  
  927. pascal OSErr SetUserPaneSetUpSpecialBackgroundProc (ControlHandle control, ControlUserPaneBackgroundProcPtr upp)
  928. {
  929.     //
  930.     //    This is just convenience glue to avoid stringing a bunch of long
  931.     //    constant names together all the time.
  932.     //
  933.  
  934.     OSErr err = noErr;
  935.  
  936.     if (!MoreAssert (HaveAppearance ( )))
  937.         err = paramErr;
  938.     else if (!MoreAssertControlHasDefProcResID (control,kControlUserPaneDefProcResID))
  939.         err = paramErr;
  940.     else
  941.         err = SetControlData (control,kControlNoPart,kControlUserPaneBackgroundProcTag,sizeof(upp),(Ptr)&upp);
  942.  
  943.     return err;
  944. }
  945.  
  946. static pascal OSErr GetEnclosingBounds (ControlHandle control, Rect *bounds)
  947. {
  948.     //
  949.     //    Gets the bounds of the specified control's super-control.
  950.     //    the the specified control has no super-control or the specified
  951.     //    control's super control is the root control (whose bounds
  952.     //    encompass all of QuickDraw, which is usually not useful), this
  953.     //    function gets the bounds of the owning window.
  954.     //
  955.  
  956.     OSErr err = noErr;
  957.  
  958.     if (!MoreAssert (control && *control && (**control).contrlOwner))
  959.         err = nilHandleErr;
  960.     else if (!HaveAppearance ( ))
  961.         *bounds = ((**control).contrlOwner)->portRect;
  962.     else
  963.     {
  964.         ControlHandle superControl;
  965.  
  966.         err = GetSuperControl (control,&superControl);
  967.  
  968.         if (err == errNoRootControl)
  969.         {
  970.             *bounds = ((**control).contrlOwner)->portRect;
  971.             err = noErr;
  972.         }
  973.         else if (!err)
  974.         {
  975.             ControlHandle rootControl;
  976.  
  977.             if (!(err = GetRootControl ((**control).contrlOwner, &rootControl)))
  978.             {
  979.                 if (rootControl == superControl)
  980.                     *bounds = ((**control).contrlOwner)->portRect;
  981.                 else
  982.                     *bounds = (**superControl).contrlRect;
  983.             }
  984.         }
  985.     }
  986.  
  987.     return err;
  988. }
  989.  
  990. pascal OSErr ExpandControl (ControlHandle control)
  991. {
  992.     //
  993.     //    Expands the bounds of the specified control to fill its enclosing rectangle.
  994.     //
  995.  
  996.     OSErr err = noErr;
  997.  
  998.     if (!MoreAssert (control && *control))
  999.         err = nilHandleErr;
  1000.     else
  1001.     {
  1002.         Rect bounds;
  1003.  
  1004.         if (!(err = GetEnclosingBounds (control,&bounds)))
  1005.         {
  1006.             MoveControl (control, bounds.left, bounds.top);
  1007.             SizeControl (control, bounds.right - bounds.left, bounds.bottom - bounds.top);
  1008.         }
  1009.     }
  1010.  
  1011.     return err;
  1012. }
  1013.  
  1014. pascal OSErr InsetControl (ControlHandle control, SInt16 byHowMuch)
  1015. {
  1016.     //
  1017.     //    Insets a control just like InsetRect insets a Rect.
  1018.     //
  1019.  
  1020.     OSErr err = noErr;
  1021.  
  1022.     if (!MoreAssert (control && *control))
  1023.         err = nilHandleErr;
  1024.     else
  1025.     {
  1026.         Rect contrlRect = (**control).contrlRect;
  1027.  
  1028.         InsetRect (&contrlRect, byHowMuch, byHowMuch);
  1029.         MoveControl (control, contrlRect.left, contrlRect.top);
  1030.         SizeControl (control, contrlRect.right - contrlRect.left, contrlRect.bottom - contrlRect.top);
  1031.     }
  1032.  
  1033.     return err;
  1034. }
  1035.  
  1036. pascal OSErr OffsetControl (ControlHandle control, SInt16 h, SInt16 v)
  1037. {
  1038.     //
  1039.     //    Offsets a control just like OffsetRect offsets a Rect.
  1040.     //
  1041.  
  1042.     OSErr err = noErr;
  1043.  
  1044.     if (!MoreAssert (control && *control))
  1045.         err = nilHandleErr;
  1046.     else
  1047.     {
  1048.         Rect contrlRect = (**control).contrlRect;
  1049.         OffsetRect (&contrlRect,h,v);
  1050.         MoveControl (control,contrlRect.left,contrlRect.top);
  1051.     }
  1052.  
  1053.     return err;
  1054. }
  1055.  
  1056. pascal OSErr MoveControlNear (ControlHandle moveMe, ControlHandle near, MoreControls_tDirection dir)
  1057. {
  1058.     //
  1059.     //    Moves one control near another. If the controls both have radio behavior,
  1060.     //    the distance between them is 0. If at least one of the controls does not
  1061.     //    have radio behavior, the distance between the two controls is non-zero
  1062.     //    (but not large; see definition of MoreControls_kControlGap).
  1063.     //
  1064.     //    To understand the relative positioning, see the comments in MoreControls_tDirection.
  1065.     //
  1066.     //    Right now this function only supports north, south, east, and west.
  1067.     //    I can't decide what it means to move a control to the southwest of another.
  1068.     //
  1069.  
  1070.     OSErr err = noErr;
  1071.  
  1072.     if (!MoreAssert (moveMe && near && *moveMe && *near))
  1073.         err = nilHandleErr;
  1074.     else if (!MoreAssert ((**moveMe).contrlOwner == (**near).contrlOwner))
  1075.         err = paramErr;
  1076.     else if (!MoreAssert (dir == MoreControls_kDirectionNorth || dir == MoreControls_kDirectionSouth ||
  1077.         dir == MoreControls_kDirectionEast || dir == MoreControls_kDirectionWest))
  1078.     {
  1079.         err = paramErr;
  1080.     }
  1081.     else
  1082.     {
  1083.         Rect    moveMeRect        = (**moveMe).contrlRect,
  1084.                 nearRect        = (**near).contrlRect;
  1085.         UInt16    moveMeWidth        = moveMeRect.right - moveMeRect.left,
  1086.                 moveMeHeight    = moveMeRect.bottom - moveMeRect.top,
  1087.                 nearWidth        = nearRect.right - nearRect.left,
  1088.                 nearHeight        = nearRect.bottom - nearRect.top;
  1089.  
  1090.         switch (dir)
  1091.         {
  1092.             case MoreControls_kDirectionNorth    :
  1093.             case MoreControls_kDirectionSouth    :
  1094.  
  1095.                 moveMeRect.left        = nearRect.left + (nearWidth / 2) - (moveMeWidth / 2);
  1096.                 moveMeRect.right    = moveMeRect.left + moveMeWidth;
  1097.  
  1098.                 break;
  1099.  
  1100.             case MoreControls_kDirectionEast    :
  1101.             case MoreControls_kDirectionWest    :
  1102.  
  1103.                 moveMeRect.top        = nearRect.top + (nearHeight / 2) - (moveMeHeight / 2);
  1104.                 moveMeRect.bottom    = moveMeRect.top + moveMeHeight;
  1105.  
  1106.                 break;
  1107.         }
  1108.  
  1109.         switch (dir)
  1110.         {
  1111.             case MoreControls_kDirectionWest    :
  1112.  
  1113.                 moveMeRect.right    = nearRect.left - MoreControls_kControlGap;
  1114.                 moveMeRect.left        = moveMeRect.right - moveMeWidth;
  1115.                 break;
  1116.  
  1117.             case MoreControls_kDirectionEast    :
  1118.  
  1119.                 moveMeRect.left        = nearRect.right + MoreControls_kControlGap;
  1120.                 moveMeRect.right    = moveMeRect.left + moveMeWidth;
  1121.                 break;
  1122.  
  1123.             case MoreControls_kDirectionNorth    :
  1124.  
  1125.                 moveMeRect.bottom    = nearRect.top - MoreControls_kControlGap;
  1126.                 moveMeRect.top        = moveMeRect.bottom - moveMeHeight;
  1127.  
  1128.                 break;
  1129.  
  1130.             case MoreControls_kDirectionSouth    :
  1131.  
  1132.                 moveMeRect.top        = nearRect.bottom + MoreControls_kControlGap;
  1133.                 moveMeRect.bottom    = moveMeRect.top + moveMeHeight;
  1134.  
  1135.                 break;
  1136.         }
  1137.  
  1138.         MoveControl (moveMe, moveMeRect.left, moveMeRect.top); // finally, the pay-off!
  1139.     }
  1140.  
  1141.     return err;
  1142. }
  1143.  
  1144. pascal OSErr SetControlQuadrant (ControlHandle control, MoreControls_tDirection quadrant)
  1145. {
  1146.     //
  1147.     //    Moves a control to one of nine quadrants within its enclosing rectangle.
  1148.     //    To understand the positioning, see the comments in MoreControls_tDirection.
  1149.     //
  1150.  
  1151.     OSErr err = noErr;
  1152.  
  1153.     if (!(MoreAssert (quadrant >= MoreControls_kDirectionNorthWest || quadrant <= MoreControls_kDirectionSouthEast)))
  1154.         err = paramErr;
  1155.     else
  1156.     {
  1157.         Rect enclosingBounds;
  1158.  
  1159.         if (!(err = GetEnclosingBounds (control,&enclosingBounds)))
  1160.         {
  1161.             Rect    controlRect            = (**control).contrlRect;
  1162.             UInt16    controlWidth        = controlRect.right - controlRect.left,
  1163.                     controlHeight        = controlRect.bottom - controlRect.top,
  1164.                     enclosingWidth        = enclosingBounds.right - enclosingBounds.left,
  1165.                     enclosingHeight        = enclosingBounds.bottom - enclosingBounds.top;
  1166.  
  1167.             switch (quadrant)
  1168.             {
  1169.                 case MoreControls_kDirectionNorthWest    :
  1170.                 case MoreControls_kDirectionNorth        :
  1171.                 case MoreControls_kDirectionNorthEast    :
  1172.  
  1173.                     controlRect.top        = enclosingBounds.top;
  1174.                     controlRect.bottom    = enclosingBounds.top + controlHeight;
  1175.                     break;
  1176.  
  1177.                 case MoreControls_kDirectionSouthWest    :
  1178.                 case MoreControls_kDirectionSouth        :
  1179.                 case MoreControls_kDirectionSouthEast    :
  1180.  
  1181.                     controlRect.bottom    = enclosingBounds.bottom;
  1182.                     controlRect.top        = enclosingBounds.bottom - controlHeight;
  1183.                     break;
  1184.  
  1185.                 case MoreControls_kDirectionWest        :
  1186.                 case MoreControls_kDirectionCenter        :
  1187.                 case MoreControls_kDirectionEast        :
  1188.  
  1189.                     controlRect.top        = enclosingBounds.top + (enclosingHeight / 2) - (controlHeight / 2);
  1190.                     controlRect.bottom    = controlRect.top + controlHeight;
  1191.                     break;
  1192.             }
  1193.  
  1194.             switch (quadrant)
  1195.             {
  1196.                 case MoreControls_kDirectionNorthWest    :
  1197.                 case MoreControls_kDirectionWest        :
  1198.                 case MoreControls_kDirectionSouthWest    :
  1199.  
  1200.                     controlRect.left    = enclosingBounds.left;
  1201.                     controlRect.right    = enclosingBounds.left + controlWidth;
  1202.                     break;
  1203.  
  1204.                 case MoreControls_kDirectionNorthEast    :
  1205.                 case MoreControls_kDirectionEast        :
  1206.                 case MoreControls_kDirectionSouthEast    :
  1207.  
  1208.                     controlRect.right    = enclosingBounds.right;
  1209.                     controlRect.left    = enclosingBounds.right - controlWidth;
  1210.                     break;
  1211.  
  1212.                 case MoreControls_kDirectionNorth        :
  1213.                 case MoreControls_kDirectionCenter        :
  1214.                 case MoreControls_kDirectionSouth        :
  1215.  
  1216.                     controlRect.left    = enclosingBounds.left + (enclosingWidth / 2) - (controlWidth / 2);
  1217.                     controlRect.right    = controlRect.left + controlWidth;
  1218.                     break;
  1219.             }
  1220.  
  1221.             MoveControl (control, controlRect.left, controlRect.top);
  1222.         }
  1223.     }
  1224.  
  1225.     return err;
  1226. }
  1227.  
  1228. pascal OSErr SetBestControlRect (ControlHandle control, SInt16 *baseLineOffsetP)
  1229. {
  1230.     //
  1231.     //    Takes GetBestControlRect to its logical conclusion and moves and resizes
  1232.     //    the specified control to fit its best control bounds rectangle, if any.
  1233.     //    You can pass NIL for baseLineOffsetP if you don't care to know the text
  1234.     //    baseline for the control. This function should probably produce a value
  1235.     //    denoting whether the control moved or changed size.
  1236.     //
  1237.  
  1238.     OSErr err = noErr;
  1239.  
  1240.     // it's OK for baseLineOffsetP to be NIL
  1241.  
  1242.     if (!MoreAssert (control && *control))
  1243.         err = nilHandleErr;
  1244.     else if (!MoreAssert (HaveAppearance ( )))
  1245.         err = paramErr;
  1246.     else
  1247.     {
  1248.         UInt32 features;
  1249.         err = GetControlFeatures (control, &features);
  1250.  
  1251.         if (!err && (kControlSupportsCalcBestRect & features))
  1252.         {
  1253.             Rect    best = { 0,0,0,0 };
  1254.             SInt16    dontCare;
  1255.  
  1256.             if (!baseLineOffsetP)
  1257.                 baseLineOffsetP = &dontCare;
  1258.  
  1259.             if (!(err = GetBestControlRect (control,&best,baseLineOffsetP)))
  1260.             {
  1261.                 SizeControl (control, best.right - best.left, best.bottom - best.top);
  1262.                 MoveControl (control, best.left, best.top);
  1263.             }
  1264.         }
  1265.     }
  1266.  
  1267.     return err;
  1268. }
  1269.  
  1270. pascal OSErr GetStaticTextControlTextHeight (ControlHandle control, SInt16 *textHeight)
  1271. {
  1272.     //
  1273.     //    This is just convenience glue to avoid stringing a bunch of long
  1274.     //    constant names together all the time.
  1275.     //
  1276.  
  1277.     OSErr err = noErr;
  1278.  
  1279.     if (!MoreAssert (HaveAppearance ( )))
  1280.         err = paramErr;
  1281.     else if (!MoreAssertControlHasDefProcResID (control,kControlStaticTextDefProcResID))
  1282.         err = paramErr;
  1283.     else
  1284.     {
  1285.         Size actualTextHeightSize;
  1286.         err = GetControlData (control, kControlNoPart, kControlStaticTextTextHeightTag,
  1287.             sizeof (textHeight), (Ptr) textHeight, &actualTextHeightSize);
  1288.         if (!err) (void) MoreAssert (actualTextHeightSize == sizeof (*textHeight));
  1289.     }
  1290.  
  1291.     return err;
  1292. }
  1293.  
  1294. pascal OSErr SetBevelButtonMenu (ControlHandle control, MenuRef menu)
  1295. {
  1296.     //
  1297.     //    This is just convenience glue to avoid stringing a bunch of long
  1298.     //    constant names together all the time.
  1299.     //
  1300.  
  1301.     OSErr err = noErr;
  1302.  
  1303.     if (!MoreAssert (HaveAppearance ( )))
  1304.         err = paramErr;
  1305.     else if (!MoreAssertControlHasDefProcResID (control,kControlBevelButtonDefProcResID))
  1306.         err = paramErr;
  1307.     else
  1308.     {
  1309.         //
  1310.         //    There appears to be a bug in the bevel button CDEF which prevents you
  1311.         //    from setting a menu if the control was not created with a menu, so to
  1312.         //    avoid this problem, we assert so you can tell when you are likely to
  1313.         //    run afoul of this problem. I can't remember whether I filed a bug
  1314.         //    against this misbehavior. I should check.
  1315.         //
  1316.  
  1317. #if MORE_DEBUG
  1318.  
  1319.         MenuHandle    existingMenuH;
  1320.         Size        actualSize;
  1321.  
  1322.         err = GetControlData (control, kControlNoPart, kControlBevelButtonMenuHandleTag,
  1323.             sizeof(existingMenuH), Ptr (&existingMenuH), &actualSize);
  1324.         if (err) return err;
  1325.         if (!MoreAssert (actualSize == sizeof(existingMenuH)))
  1326.             err = paramErr;
  1327.         else if (!MoreAssert (existingMenuH))
  1328.             err = nilHandleErr;
  1329.         else
  1330.  
  1331. #endif
  1332.  
  1333.         err = SetControlData (control, kControlNoPart, kControlBevelButtonMenuHandleTag,
  1334.             sizeof(MenuRef), Ptr (&menu));
  1335.     }
  1336.  
  1337.     return err;
  1338. }
  1339.  
  1340. pascal OSErr SetPopUpButtonMenu (ControlHandle control, MenuRef menu)
  1341. {
  1342.     //
  1343.     //    This is just convenience glue to avoid stringing a bunch of long
  1344.     //    constant names together all the time.
  1345.     //
  1346.  
  1347.     MoreAssert (HaveAppearance ( ));
  1348.     MoreAssert (GetAppearanceVersion ( ) >= 0x0101);
  1349.     MoreAssertControlHasDefProcResID (control,kControlPopupButtonDefProcResID);
  1350.  
  1351.     OSErr err = SetControlData (control, kControlNoPart, kControlPopupButtonMenuHandleTag,
  1352.         sizeof(MenuRef), Ptr (&menu));
  1353.     return err;
  1354. }
  1355.  
  1356. pascal OSErr SetBevelButtonMenuDelay (ControlHandle control, SInt32 delay)
  1357. {
  1358.     //
  1359.     //    This is just convenience glue to avoid stringing a bunch of long
  1360.     //    constant names together all the time.
  1361.     //
  1362.  
  1363.     OSErr err = noErr;
  1364.  
  1365.     if (!MoreAssert (HaveAppearance ( )))
  1366.         err = paramErr;
  1367.     else if (!MoreAssert (GetAppearanceVersion ( ) >= 0x0101))
  1368.         err = paramErr;
  1369.     else if (!MoreAssertControlHasDefProcResID (control,kControlBevelButtonDefProcResID))
  1370.         err = paramErr;
  1371.     else
  1372.         err = SetControlData (control, kControlNoPart, kControlBevelButtonMenuDelayTag, sizeof (delay), Ptr (&delay));
  1373.  
  1374.     return err;
  1375. }
  1376.  
  1377. pascal OSErr SetProgressBarIndeterminate (ControlHandle control, Boolean i)
  1378. {
  1379.     //
  1380.     //    This is just convenience glue to avoid stringing a bunch of long
  1381.     //    constant names together all the time.
  1382.     //
  1383.  
  1384.     OSErr err = noErr;
  1385.  
  1386.     if (!MoreAssert (HaveAppearance ( )))
  1387.         err = paramErr;
  1388.     else if (!MoreAssertControlHasDefProcResID (control,kControlProgressBarDefProcResID))
  1389.         err = paramErr;
  1390.     else if (!MoreAssert (i == true || i == false)) // talk about paranoid!
  1391.         err = paramErr;
  1392.     else
  1393.         err = SetControlData (control, kControlNoPart, kControlProgressBarIndeterminateTag, sizeof (i), Ptr (&i));
  1394.  
  1395.     return err;
  1396. }
  1397.  
  1398. pascal OSErr ToggleControl (ControlHandle control)
  1399. {
  1400.     //
  1401.     //    Given a control whose limits suggest it toggles between 0 and 1
  1402.     //    (like a check box or a radio button), this function sets the control
  1403.     //    value to the opposite of its present value.
  1404.     //
  1405.  
  1406.     OSErr err = noErr;
  1407.  
  1408.     if (!MoreAssert (control && *control))
  1409.         err = nilHandleErr;
  1410.     else if (!MoreAssert (GetControlMaximum (control) == 1))
  1411.         err = paramErr;
  1412.     else if (!MoreAssert (GetControlMinimum (control) == 0))
  1413.         err = paramErr;
  1414.     else
  1415.         SetControlValue (control, GetControlValue (control) ? 0 : 1);
  1416.  
  1417.     return err;
  1418. }
  1419.  
  1420. static pascal OSErr EmbedSubControls (ControlHandle source, ControlHandle destination, UInt16 index)
  1421. {
  1422.     //
  1423.     //    This is a helper function for EncloseSubControls; if you find a use
  1424.     //    for it elsewhere, keep in mind that the controls are embedded into
  1425.     //    the destination control in the opposite order from that in which
  1426.     //    they appear in the source control. This is fine as long as you
  1427.     //    copy the controls back with EmbedSubControls. This is just what
  1428.     //    EncloseSubControls needed. Why do we do things this way? We want
  1429.     //    to be recursive so we can easily back out of a partial embedding
  1430.     //    transfer, and this is the easiest way to do it.
  1431.     //
  1432.  
  1433.     OSErr err = noErr;
  1434.  
  1435.     if (index)
  1436.     {
  1437.         ControlHandle subControl;
  1438.  
  1439.         if (!(err = GetIndexedSubControl (source,index,&subControl)))
  1440.         if (!(err = EmbedControl (subControl,destination)))
  1441.         {
  1442.             err = EmbedSubControls (source,destination,index-1);
  1443.             if (err) (void) EmbedControl (subControl,source);
  1444.         }
  1445.     }
  1446.  
  1447.     return err;
  1448. }
  1449.  
  1450. pascal OSErr EncloseSubControls (ControlHandle parent)
  1451. {
  1452.     //
  1453.     //    Moves and resizes the parent control to enclose all its sub-controls.
  1454.     //    However, we can't just move the parent control, because the
  1455.     //    sub-controls will move along with it, which is not what we want.
  1456.     //    So we create a temporary embeddable control and move the sub-controls
  1457.     //    to this temporary control while we move the parent control.
  1458.     //    Note that EmbedSubControls reverses the order of the sub-controls,
  1459.     //    which is fine for our purposes, since we call it twice.
  1460.     //
  1461.  
  1462.     OSErr err = noErr;
  1463.  
  1464.     if (!MoreAssert (parent && *parent))
  1465.         err = nilHandleErr;
  1466.     else
  1467.     {
  1468.  
  1469.         UInt16 subControlCount;
  1470.  
  1471.         if (!(err = CountSubControls (parent,&subControlCount)))
  1472.         {
  1473.             if (!MoreAssert (subControlCount > 0))
  1474.                 err = paramErr;
  1475.             else
  1476.             {
  1477.                 static Rect tempUserPaneBounds;
  1478.  
  1479.                 ControlHandle tempUserPane = NewControl ((**parent).contrlOwner, &tempUserPaneBounds, "\p", false,
  1480.                     kControlSupportsEmbedding, 0, 0, kControlUserPaneProc, 0);
  1481.  
  1482.                 if (!tempUserPane)
  1483.                     err = nilHandleErr;
  1484.                 else
  1485.                 {
  1486.                     UInt16 subControlIndex = subControlCount;
  1487.                     ControlHandle subControl;
  1488.  
  1489.                     if (!(err = GetIndexedSubControl (parent,subControlIndex,&subControl)))
  1490.                     {
  1491.                         if (!MoreAssert (subControl && *subControl))
  1492.                             err = nilHandleErr;
  1493.                         else
  1494.                         {
  1495.                             Rect parentRect = (**subControl).contrlRect;
  1496.  
  1497.                             while (--subControlIndex)
  1498.                             {
  1499.                                 err = GetIndexedSubControl (parent,subControlIndex,&subControl);
  1500.                                 if (err) break;
  1501.  
  1502.                                 if (!MoreAssert (subControl && *subControl))
  1503.                                 {
  1504.                                     err = nilHandleErr;
  1505.                                     break;
  1506.                                 }
  1507.  
  1508.                                 Rect contrlRect = (**subControl).contrlRect;
  1509.  
  1510.                                 if (contrlRect.bottom > parentRect.bottom)
  1511.                                     parentRect.bottom = contrlRect.bottom;
  1512.                                 if (contrlRect.right > parentRect.right)
  1513.                                     parentRect.right = contrlRect.right;
  1514.                                 if (contrlRect.left < parentRect.left)
  1515.                                     parentRect.left = contrlRect.left;
  1516.                                 if (contrlRect.top < parentRect.top)
  1517.                                     parentRect.top = contrlRect.top;
  1518.                             }
  1519.  
  1520.                             if (!err && !(err = EmbedSubControls (parent,tempUserPane,subControlCount)))
  1521.                             {
  1522.                                 InsetRect (&parentRect, -(MoreControls_kControlGap), -(MoreControls_kControlGap));
  1523.                                 MoveControl (parent, parentRect.left, parentRect.top);
  1524.                                 SizeControl (parent, parentRect.right - parentRect.left, parentRect.bottom - parentRect.top);
  1525.                                 OSErr err2 = EmbedSubControls (tempUserPane,parent,subControlCount);
  1526.                                 if (!err) err2 = err;
  1527.                             }
  1528.                         }
  1529.                     }
  1530.                     DisposeControl (tempUserPane);
  1531.                 }
  1532.             }
  1533.         }
  1534.     }
  1535.     return err;
  1536. }
  1537.  
  1538. pascal OSErr AlignControl (ControlHandle anchor, ControlHandle moveMe, MoreControls_tAlignment align)
  1539. {
  1540.     //
  1541.     //    Aligns one control (moveMe) to an edge of another (anchor).
  1542.     //    For an explanation of MoreControls_tAlignment, see the header.
  1543.     //
  1544.  
  1545.     OSErr err = noErr;
  1546.  
  1547.     if (!MoreAssert (moveMe && *moveMe && anchor && *anchor))
  1548.         err = nilHandleErr;
  1549.     else if (!MoreAssert (align == MoreControls_kAlignSystem || align == MoreControls_kAlignLeft ||
  1550.         align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight))
  1551.     {
  1552.         err = paramErr;
  1553.     }
  1554.     else
  1555.     {
  1556.         align = FilterAlignment (align);
  1557.  
  1558.         if (!MoreAssert (align == MoreControls_kAlignLeft ||
  1559.             align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight))
  1560.         {
  1561.             err = paramErr;
  1562.         }
  1563.         else
  1564.         {
  1565.             short horiz, vert = (**moveMe).contrlRect.top;
  1566.  
  1567.             switch (align)
  1568.             {
  1569.                 case MoreControls_kAlignLeft    :
  1570.  
  1571.                     horiz = (**anchor).contrlRect.left;
  1572.                     break;
  1573.  
  1574.                 case MoreControls_kAlignCenter    :
  1575.  
  1576.                     (void) MoreAssert (false && MoreControls_kAlignCenter); // untraced logic
  1577.                     break;
  1578.  
  1579.                 case MoreControls_kAlignRight    :
  1580.  
  1581.                     (void) MoreAssert (false && MoreControls_kAlignRight); // untraced logic
  1582.                     horiz = (**anchor).contrlRect.right -
  1583.                         ((**moveMe).contrlRect.right - (**moveMe).contrlRect.left);
  1584.                     break;
  1585.             }
  1586.  
  1587.             MoveControl (moveMe, horiz, vert);
  1588.         }
  1589.     }
  1590.  
  1591.     return err;
  1592. }
  1593.  
  1594. pascal OSErr AlignSubControls (ControlHandle parent, MoreControls_tAlignment align)
  1595. {
  1596.     //
  1597.     //    Given a parent control, aligns all its sub-controls along the most
  1598.     //    extreme edge. So if left alignment is specified, this function finds
  1599.     //    the sub-control which is leftmost and aligns the rest of the sub-controls
  1600.     //    to its left edge. For an explanation of MoreControls_tAlignment, see the header.
  1601.     //
  1602.  
  1603.     OSErr err = noErr;
  1604.  
  1605.     if (!MoreAssert (parent && *parent))
  1606.         err = nilHandleErr;
  1607.     else if (!MoreAssert (align == MoreControls_kAlignSystem || align == MoreControls_kAlignLeft ||
  1608.         align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight))
  1609.     {
  1610.         err = paramErr;
  1611.     }
  1612.     else
  1613.     {
  1614.         align = FilterAlignment (align);
  1615.  
  1616.         if (!MoreAssert (align == MoreControls_kAlignLeft ||
  1617.             align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight))
  1618.         {
  1619.             err = paramErr;
  1620.         }
  1621.         else
  1622.         {
  1623.  
  1624.             UInt16 subControlCount;
  1625.  
  1626.             if (!(err = CountSubControls (parent,&subControlCount)))
  1627.             {
  1628.                 if (!MoreAssert (subControlCount > 0))
  1629.                     err = paramErr;
  1630.                 else if (subControlCount == 1)
  1631.                 {
  1632.                     (void) MoreAssert (false && subControlCount == 1); // untraced logic
  1633.                 }
  1634.                 else
  1635.                 {
  1636.                     UInt16 subControlIndex = subControlCount;
  1637.                     ControlHandle subControl;
  1638.  
  1639.                     if (!(err = GetIndexedSubControl (parent,subControlIndex,&subControl)))
  1640.                     {
  1641.                         if (!MoreAssert (subControl && *subControl))
  1642.                             err = nilHandleErr;
  1643.                         else
  1644.                         {
  1645.                             ControlHandle anchor = subControl;
  1646.  
  1647.                             while (--subControlIndex)
  1648.                             {
  1649.                                 err = GetIndexedSubControl (parent,subControlIndex,&subControl);
  1650.                                 if (err) break;
  1651.  
  1652.                                 switch (align)
  1653.                                 {
  1654.                                     case MoreControls_kAlignLeft    :
  1655.  
  1656.                                         if ((**subControl).contrlRect.left < (**anchor).contrlRect.left)
  1657.                                             anchor = subControl;
  1658.                                         break;
  1659.  
  1660.                                     case MoreControls_kAlignCenter    :
  1661.  
  1662.                                         (void) MoreAssert (false && MoreControls_kAlignCenter); // untraced logic
  1663.                                         break;
  1664.  
  1665.                                     case MoreControls_kAlignRight    :
  1666.  
  1667.                                         if ((**subControl).contrlRect.right > (**anchor).contrlRect.right)
  1668.                                             anchor = subControl;
  1669.                                         break;
  1670.                                 }
  1671.                             }
  1672.  
  1673.                             if (!err)
  1674.                             {
  1675.                                 subControlIndex = subControlCount;
  1676.  
  1677.                                 do
  1678.                                 {
  1679.                                     err = GetIndexedSubControl (parent,subControlIndex,&subControl);
  1680.                                     if (err) break;
  1681.  
  1682.                                     if (subControl != anchor)
  1683.                                     {
  1684.                                         short horiz, vert = (**subControl).contrlRect.top;
  1685.  
  1686.                                         switch (align)
  1687.                                         {
  1688.                                             case MoreControls_kAlignLeft    :
  1689.  
  1690.                                                 horiz = (**anchor).contrlRect.left;
  1691.                                                 break;
  1692.  
  1693.                                             case MoreControls_kAlignCenter    :
  1694.  
  1695.                                                 (void) MoreAssert (false && MoreControls_kAlignCenter); // untraced logic
  1696.                                                 break;
  1697.  
  1698.                                             case MoreControls_kAlignRight    :
  1699.  
  1700.                                                 horiz = (**anchor).contrlRect.right -
  1701.                                                     ((**subControl).contrlRect.right - (**subControl).contrlRect.left);
  1702.                                                 break;
  1703.                                         }
  1704.  
  1705.                                         MoveControl (subControl,horiz,vert);
  1706.                                     }
  1707.                                 }
  1708.                                 while (--subControlIndex);
  1709.                             }
  1710.                         }
  1711.                     }
  1712.                 }
  1713.             }
  1714.         }
  1715.     }
  1716.  
  1717.     return err;
  1718. }
  1719.  
  1720. pascal OSErr IdleControlsInAllWindows (void)
  1721. {
  1722.     //
  1723.     //    For each visible window in the window list, this function gives
  1724.     //    the controls a chance to idle.
  1725.     //
  1726.  
  1727.     OSErr err = noErr;
  1728.  
  1729.     if (!MoreAssert (HaveAppearance ( )))
  1730.         err = paramErr;
  1731.     else if (WindowPtr window = FrontWindow ( ))
  1732.     {
  1733.         IdleControls (window);
  1734.  
  1735.         window = GetNextWindow (window);
  1736.  
  1737.         while (window)
  1738.         {
  1739.             if (IsWindowVisible (window))
  1740.                 IdleControls (window);
  1741.  
  1742.             window = GetNextWindow (window);
  1743.         }
  1744.     }
  1745.  
  1746.     return err;
  1747. }
  1748.  
  1749. pascal OSErr SetCursorAccordingToControl (QDGlobalsPtr qd)
  1750. {
  1751.     //
  1752.     //    Sets the cursor according to which control the cursor
  1753.     //    is floating over. In a perfect world, we'd be able to
  1754.     //    ask the control to set the cursor for us, but in the
  1755.     //    real world we just guess.
  1756.     //
  1757.  
  1758.     OSErr err = noErr;
  1759.  
  1760.     WindowPtr window = FrontWindow ( );
  1761.  
  1762.     if (!window)
  1763.         SetCursor (&(qd->arrow));
  1764.     else
  1765.     {
  1766.         GrafPtr preservedPort;
  1767.  
  1768.         GetPort (&preservedPort);
  1769.         SetPort (window);
  1770.  
  1771.         Point mouse;
  1772.         GetMouse (&mouse);
  1773.  
  1774.         ControlPartCode        cpc;
  1775.         ControlHandle        whichControl;
  1776.  
  1777.         err = MoreFindControlUnderMouse (mouse, window, &cpc, &whichControl);
  1778.  
  1779.         if (!err)
  1780.         {
  1781.             if (!whichControl || cpc == kControlNoPart || !HaveAppearance ( ))
  1782.                 SetCursor (&(qd->arrow));
  1783.             else
  1784.             {
  1785.                 short resID;
  1786.  
  1787.                 if (!(err = GetControlDefProcResID (whichControl, &resID)))
  1788.                 {
  1789.                     if (resID == kControlEditTextDefProcResID)
  1790.                         SetCursor (*GetCursor (iBeamCursor));
  1791.                     else
  1792.                         SetCursor (&(qd->arrow));
  1793.                 }
  1794.             }
  1795.         }
  1796.  
  1797.         SetPort (preservedPort);
  1798.     }
  1799.  
  1800.     return err;
  1801. }
  1802.  
  1803. pascal OSErr IsScrollBar (ControlHandle ch, Boolean *isScrollBar)
  1804. {
  1805.     //
  1806.     //    Tests whether a given control is a scroll bar.
  1807.     //    Replaces bogus code from AppsToGo. TO DO: find
  1808.     //    a better abstraction for this. Waiting for
  1809.     //    developer feedback. This function may go away.
  1810.     //
  1811.  
  1812.     OSErr err = noErr;
  1813.  
  1814.     if (!MoreAssert (ch && *ch && isScrollBar))
  1815.         err = nilHandleErr;
  1816.     else
  1817.     {
  1818.         short resID;
  1819.  
  1820.         if (!(err = GetControlDefProcResID (ch, &resID)))
  1821.         {
  1822.             *isScrollBar = (resID == kControlOldScrollBarDefProcResID || resID == kControlScrollBarDefProcResID);
  1823.         }
  1824.     }
  1825.  
  1826.     return err;
  1827. }
  1828.